<?php

declare(strict_types=1);
/**
 * 扩展自定义编译器支持
 * Author: Zane
 * Email: 873934580@qq.com
 * Date: 2022/4/13.
 */

namespace Zane\View\Compiler;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\View\Compilers\BladeCompiler;
use InvalidArgumentException;
use Zane\View\Compiler\Concerns\compileAd;
use Zane\View\Compiler\Concerns\compileArchives;
use Zane\View\Compiler\Concerns\compileCate;
use Zane\View\Compiler\Concerns\compileField;
use Zane\View\Compiler\Concerns\compileSystem;
use Zane\View\Compiler\Concerns\compileTag;
use Zane\View\Compiler\Concerns\compileType;

class ZaneCompiler extends BladeCompiler
{
    use compileAd;
    use compileArchives;
    use compileCate;
    use compileField;
    use compileSystem;
    use compileTag;
    use compileType;

    /**
     * 确定给定路径上的视图是否已过期。
     *
     * @param $path
     *
     * @return bool
     */
    public function isExpired($path): bool
    {
        //TODO 测试阶段时模版总是需要重新编译的
        if (config('app.debug')) {
            return true;
        }

        return parent::isExpired($path);
    }

    /**
     * 注册自定义指令的处理程序。
     *
     * @param $name
     * @param callable $handler
     */
    public function directive($name, callable $handler): void
    {
        try {
            parent::directive($name, $handler);
        } catch (InvalidArgumentException $e) {
            if (!preg_match('/^\w+(?::\w+)?$/x', $name)) {
                throw new InvalidArgumentException("The directive name [{$name}] is not valid. Directive names must only contain alphanumeric characters and underscores.");
            }
            $this->customDirectives[$name] = $handler;
        }
    }

    /**
     * 编译以“@”开头的Blade语句。
     *
     * @param $value
     *
     * @return string
     */
    protected function compileStatements($value): string
    {
        $content = preg_replace_callback(
            '/\B@(@?\w+(?::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x',
            function ($match) {
                return $this->compileStatement($match);
            },
            $value
        );

        return parent::compileStatements($content);
    }

    /**
     * @param $match
     *
     * @return string
     */
    protected function compileStatement($match): string
    {
        if (Str::contains($match[1], config('view.prefix'))) {
            $action = Str::after($match[1], config('view.prefix'));
            $action = $this->parserEnd($action);
            $compileAction = 'compile'.ucfirst($action);
            if (method_exists($this, $compileAction)) {
                return $this->{$compileAction}(Arr::get($match, 3));
            }
            if (isTemplateTag(lcfirst($action))) {
                return $this->customCompile(Arr::get($match, 3), $action);
            }
        }

        return parent::compileStatement($match);
    }

    protected function parserEnd($action): string
    {
        $temp = strtolower(substr($action, 0, 3));
        if ('end' === $temp) {
            return 'end';
        }

        return $action;
    }

    /**
     * 返回指令别名.
     *
     * @param $name
     *
     * @return string
     */
    protected function getCompileAlias($name): string
    {
        return $name;
    }

    /**
     * 结束编译器.
     *
     * @param $expression
     *
     * @return string
     */
    protected function compileEnd($expression): string
    {
        return '<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop();  ?>';
    }

    /**
     * 自定义编译器.
     *
     * @param $expression
     * @param $loopData
     *
     * @return string
     */
    protected function customCompile($expression, $loopData): string
    {
        $parse = ParamParse::make($expression);
        $initLoop = "<?php \$__currentLoopData = \\Zane\\View\\Service\\ReadHandle::read('{$loopData}', {$parse->getExpression()}); \$__env->addLoop(\$__currentLoopData);?>";

        $iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();';
        $empty = '';
        if ('' !== $parse->getEmpty() && null !== $parse->getEmpty()) {
            $char = mb_substr($parse->getEmpty(), 0, 1, 'UTF-8');
            if ('$' === $char) {
                $empty = "<?php if (!count(\$__currentLoopData)): echo e({$parse->getEmpty()}); endif;?>";
            } else {
                $empty = "<?php if (!count(\$__currentLoopData)): echo e('{$parse->getEmpty()}'); endif;?>";
            }
        }

        return "{$initLoop} {$empty} <?php foreach(\$__currentLoopData as {$parse->getIteration()}): {$iterateLoop} ?>";
    }
}
